/*
 * Copyright (c) 1994, 2013, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

package java.io;

/**
 * InputStream，这个抽象类是所有代表字节输入流的类的父类。
 * 
 * <p>需要定义InputStream的子类的程序，必须始终提供返回下一个输入字节的方法。
 *
 * @author  Arthur van Hoff
 * @see     java.io.BufferedInputStream
 * @see     java.io.ByteArrayInputStream
 * @see     java.io.DataInputStream
 * @see     java.io.FilterInputStream
 * @see     java.io.InputStream#read()
 * @see     java.io.OutputStream
 * @see     java.io.PushbackInputStream
 * @since   JDK1.0
 */
public abstract class InputStream implements Closeable {

    // MAX_SKIP_BUFFER_SIZE 被用来决定当跳过时，最大的跳过缓存大小，2048。
    private static final int MAX_SKIP_BUFFER_SIZE = 2048;

    /**
     * 1个位，1bit。一个字节，8个bit，1byte，对应2^8=256。
     * 读取这个输入流的下一个字节的数据。字节的值以一个int类型返回，范围从0-255之间。
     * 如果到了流的结尾，从而没有字节了，返回-1.
     * 这个方法会阻塞，直到输入数据可用，或者碰到流的结尾，或者抛出一个异常
     *
     * <p> 子类必须提供一个该方法的实现
     *
     * @return    下一个字节的数据，或者当遇到流的结尾，返回-1
     * @exception  IOException  当io异常发生时
     */
    public abstract int read() throws IOException;

    /**
     * 从输入流中读取一定数量的字节并将其存储到缓冲区数组b中。
     * 实际读取的字节数作为整数返回。此方法将一直阻塞，直到输入数据可用、检测到文件结束或抛出异常为止。
     *
     * <p>如果b的长度为0，则不读取字节，返回0;
     * 否则，将尝试读取至少一个字节。
     * 如果由于流位于文件的末尾而没有可用的字节，则返回-1值;
     * 否则，至少有一个字节被读取并存储到b中。
     *
     * <p> 读取的第一个字节被存储到元素b[0]中，然后下一个字节存储到元素b[1]中，
     * 以此类推。读取的字节数最多等于b的长度。
     * 设k为实际读取的字节数;这些字节将存储在元素b[0]到b[k-1]中，元素b[k]到b[b.length-1]中，不受影响。
     *
     * <p>类inputstream的read(b,0, b.length)方法具有相同的效果:
     *
     * @param      b   数据被读取后，进入的缓存
     * @return    读入缓存的总字节数，或因为遇到流的结尾，没有更多数据，返回-1
     * @exception  IOException  如果第一个byte因为任何原因不能访问，除非遇到了文件的结尾。如果输入流已经被关闭，或者遇到了其他的io异常
     * @exception  NullPointerException  if <code>b</code> is <code>null</code>.
     * @see        java.io.InputStream#read(byte[], int, int)
     */
    public int read(byte b[]) throws IOException {
    	// 仅仅调用下面的read方法
        return read(b, 0, b.length);
    }

    /**
     * 将最多len字节的数据，从输入流中读取到一个字节数组。
     * 尝试读取最多len字节，但是可能会读取更少的字节。
     * 实际读取的字节数作为int返回。
     *
     * <p> 此方法将一直阻塞，直到输入数据可用、检测到文件结束或抛出异常为止。
     *
     * <p> 如果len是零，那么没有字节被读取和返回0;
     * 否则，将尝试读取至少一个字节。
     * 如果没有字节可用，因为流在结束文件，值-1被返回;
     * 否则，至少有一个字节被读取并存储到b中。
     *
     * <p> 读取的第一个字节被存储到元素b[off]中，第二个字节存储到元素b[off+1]中，以此类推。
     * 读取的字节数，最多等于len。
     * 设k是实际读取的字节数;
     * 这些字节将被存储在元素b[off]到b[off+k-1]中，而元素b[off+k]到b[off+len-1]不受影响
     *
     * <p> 在每种情况下，元素b[0]到b[off]和元素b[off+len]到b[b.length-1]不受影响。
     *
     * <p> 类InputStream的read(b, off, len)方法只是重复调用read()方法。
     * 如果第一个这样的调用产生了一个IOException，那么这个异常将从read(b, off, len)方法的调用中返回。
     * 如果对read()的任何后续调用导致一个IOException，则捕获该异常，并将其视为文件结束;
     * 到此为止读取的字节被存储到b中，并返回发生异常之前读取的字节数。
     * 这个方法的默认实现会被阻塞，直到读取了len请求的输入数据量，检测到文件结束，或者抛出异常。
     * 鼓励子类提供这个方法的更有效的实现。
     *
     * @param      b    数据被读取后，进入的缓存
     * @param      off  数组b里，写入的第一个元素的位置
     * @param      len   读入缓存的最大字节数
     * @return     读入缓存的总字节数，或因为遇到流的结尾，没有更多数据，返回-1
     * @exception  IOException 如果第一个byte因为任何原因不能访问，除非遇到了文件的结尾。如果输入流已经被关闭，或者遇到了其他的io异常
     * @exception  NullPointerException   如果b是null
     * @exception  IndexOutOfBoundsException    如果off为负数，len为负数，或者len大于b.length-off
     * @see        java.io.InputStream#read()
     */
    public int read(byte b[], int off, int len) throws IOException {
    	// 对b，off，len进行输入校验
        if (b == null) {
            throw new NullPointerException();
        } else if (off < 0 || len < 0 || len > b.length - off) {
            throw new IndexOutOfBoundsException();
        } else if (len == 0) {
            return 0;
        }

        int c = read();
        if (c == -1) {
            return -1;
        }
        // 第一个读入的c，放到off位置
        b[off] = (byte)c;

        int i = 1;
        try {
        	// 最多读取len次
            for (; i < len ; i++) {
                c = read();
                if (c == -1) {
                    break;
                }
                // 依次放入
                b[off + i] = (byte)c;
            }
        } catch (IOException ee) {
        	// 如果之间有异常了，直接返会i
        }
        return i;
    }

    /**
     * 跳过这个输入流并丢弃n个字节的数据。
     * 由于各种原因，skip方法最终可能跳过一些较小的字节数，可能为0。
     * 这可能是由于若干条件中的任何一种;
     * 在跳过n个字节之前到达文件的末尾只是一种可能。
     * 将返回跳过的实际字节数。
     * 如果n为负数，类InputStream的skip方法总是返回0，并且没有跳过字节。
     * 子类可能以不同的方式处理负值。
     *
     * <p> 这个类的skip方法创建一个byte数组，
     * 然后重复读入该数组，直到读取了n个字节或者到达流的末尾。
     * 鼓励子类提供这个方法的更有效的实现。例如，实现可能依赖于查找的能力。
     *
     * @param      n   跳过的字节数
     * @return     实际跳过的字节数
     * @exception  IOException  如果流不支持查找，或者一些其他的io异常发生
     */
    public long skip(long n) throws IOException {

        long remaining = n;
        int nr;

        if (n <= 0) {
            return 0;
        }
        // 读入的数组，最大为MAX_SKIP_BUFFER_SIZE，2048，或者remaining
        int size = (int)Math.min(MAX_SKIP_BUFFER_SIZE, remaining);
        byte[] skipBuffer = new byte[size];
        
        while (remaining > 0) {
        	// 调用read(byte[],off,len)方法，最大为size或者remaining
            nr = read(skipBuffer, 0, (int)Math.min(size, remaining));
            if (nr < 0) {
                break;
            }
            // remaining不断减少
            remaining -= nr;
        }
        // 返回实际跳过的
        return n - remaining;
    }

    /**
     * 返回可以从该输入流读取(或跳过)的字节的估计数，而不会被该输入流的方法的下一次调用阻塞。
     * 下一个调用可能是同一个线程，也可能是另一个线程。
     * 单个读取或跳过这许多字节不会阻塞，但可能读取或跳过更少的字节。
     *
     * <p> 注意，虽然InputStream的一些实现将返回流中的总字节数，但很多实现不会。
     * 使用此方法的返回值，来分配用于保存此流中所有数据的缓冲区是不正确的。
     *
     * <p> 如果通过调用close()方法关闭了这个输入流，
     * 那么这个方法的子类可以选择抛出IOException。
     *
     * <p> 类InputStream的available方法总是返回0。
     *
     * <p> 这个方法应该被子类覆盖。
     *
     * @return    在不阻塞或到达输入流末尾时可以从该输入流读取(或跳过)的字节数的估计数。
     * @exception  IOException if an I/O error occurs.
     */
    public int available() throws IOException {
        return 0;
    }

    /**
     * 关闭该输入流并释放与该流关联的任何系统资源。
     *
     * <p> InputStream的close方法什么也不做。
     *
     * @exception  IOException  if an I/O error occurs.
     */
    public void close() throws IOException {}

    /**
     * 标记此输入流中的当前位置。
     * 对reset方法的后续调用将此流重新定位到最后的markedposition，
     * 以便后续读取将重新读取相同的字节。
     *
     * <p> readlimit参数告诉输入流在标记位置失效之前允许读取那么多字节。
     *
     * <p> 标记的一般约定是，如果markSupported方法返回true，
     * 则somehowstream将记住调用后读取的所有字节，并随时准备在方法reset被调用时再次提供这些相同的字节。
     * 但是，如果在调用重置之前从流中读取的字节超过了readlimit，则流根本不需要记住任何数据。
     *
     * <p> 标记一个关闭的流不应该对流有任何影响。
     *
     * <p>  InputStream的标记方法什么都不做。
     *
     * @param   readlimit  在标记位置无效之前可以读取的最大字节限制
     * @see     java.io.InputStream#reset()
     */
    public synchronized void mark(int readlimit) {}

    /**
     * 将此流重新定位到最后一次在此输入流上调用mark方法时的位置。
     *
     * <p> reset 的通常约定是：
     *
     * <ul>
     * <li> 如果markSupported方法返回true，则：
     *
     *     <ul><li> 如果方法标记在流创建后没有被调用，
     *     或者在最后一次调用后，从流中读取的字节数大于mark方法的参数，
     *     那么IOException可能会被抛出。
     *
     *     <li> 如果没有抛出IOException,然后流被复位到一个状态,
     *     自从最近的对mark方法的调用，读取的所有字节（或者如果mark方法没有被调用，从文件的开头）
     *     将被补充到接下来对read方法的调用，遵循任何字节,除非下一次对reset调用
     *     将resuppliedto后续调用者的阅读方法,否则会被下一个输入数据,调用重置时间。</ul>
     *
     * <li> 如果markSupported方法返回false，则：
     *
     *     <ul><li> 调用reset方法可能抛出一个IOException
     *
     *     <li> 如果IOException不抛出，然后流重置到一个固定的状态，这取决于输入流的类型和它是如何创建的。
     *     将被提供给read方法的后续调用者的字节，取决于输入流的特定类型。</ul></ul>
     *
     * <p>类inputstream的方法重置除了抛出一个IOException之外什么也不做。
     *
     * @exception  IOException  如果这个流没有被标记，或者这个mark已经失效
     * @see     java.io.InputStream#mark(int)
     * @see     java.io.IOException
     */
    public synchronized void reset() throws IOException {
        throw new IOException("mark/reset not supported");
    }

    /**
     * 测试此输入流是否支持mark和reset方法。
     * 是否支持mark和reset方法，是特定输入流实例的不变属性。
     * InputStream的markSupported方法返回false。
     *
     * @return  <code>true</code> 如果流实例支持mark和reset方法，返回true;否则返回false。
     * @see     java.io.InputStream#mark(int)
     * @see     java.io.InputStream#reset()
     */
    public boolean markSupported() {
        return false;
    }

}
